package com.project.shiro.credentials;

import com.project.entity.User;
import com.project.service.UserService;
import com.project.service.cryptolib.CryptoApp;
import com.project.utils.Util;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>User: Zhang Kaitao
 * <p>Date: 14-1-28
 * <p>Version: 1.0
 *  限制登录次数，如果5次出错，锁定10分钟
 */
//HashedCredentialsMatcher 在Shiro教程P47，重试次数P49
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
    @Autowired
    UserService userService;

    private static Cache<String, AtomicInteger> passwordRetryCache;

    public static Cache<String, AtomicInteger> getPasswordRetryCache(){
        return passwordRetryCache;
    }

    public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
        /**
         * 创建ehcache缓存，创建之后的有效期是10分钟；在ehcahe.xml中配置
         */
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }

    /**
     * 密码验证
     * @param token 登陆的用户信息
     * @param info 数据库里面的用户信息
     * @return
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        boolean matches=false;

        //info 来自UserReal类doGetAuthenticationInfo方法的返回值

        String username = (String) token.getPrincipal();
        //retry count + 1
        AtomicInteger retryCount = passwordRetryCache.get(username);
        if (retryCount == null) {
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(username, retryCount);
        }
        if (retryCount.incrementAndGet() > 5) {
            //if retry count > 5 throw
            throw new ExcessiveAttemptsException();
        }

        //shiro密码验证
       // matches = super.doCredentialsMatch(token, info);

        //自定义密码验证:start
        //UsernamePasswordToken封装登录用户名及密码
        UsernamePasswordToken token2 = (UsernamePasswordToken) token;
        User user = null;
        try {
            user = userService.findByUserName(token2.getUsername());
        } catch (Exception e) {
            e.printStackTrace();
        }
        String salt="";
        if(user!=null)
            salt=user.getSalt();
        //将token中的原始密码进加密
        Object tokenCredentials = null;
        try {
            tokenCredentials = CryptoApp.PwdTransValue(String.valueOf(token2.getPassword()).getBytes(), Util.hexStringToBytes(salt));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        //获取db中的用户密码
        Object accountCredentials = getCredentials(info);

        //将原始密码加密与取自DB的用户密码校验，内容一致就返回true,不一致就返回false
        matches = equals(tokenCredentials, accountCredentials);
//        //自定义密码验证:end

        if (matches) {
            //clear retry count
            passwordRetryCache.remove(username);
        }

        return matches;
    }
}
